<?php
//-------------------------------------------------------------------------------------------------------
    require_once($config['jpgraph']['path'] . 'jpgraph.php');
    require_once($config['jpgraph']['path'] . 'jpgraph_line.php');
    require_once($config['jpgraph']['path'] . 'jpgraph_bar.php');

    require_once($config['app_dir'] . '/classes/plot.view.php');
    require_once($config['app_dir'] . '/includes/db.class.php');
    require_once($config['app_dir'] . '/includes/date.class.php');
    //----------------------------------------------------------------------------------------------------
    class objDateLabelFormat {
            protected $data = false;
            protected $tdata = false;
            protected $years = false;
            protected $months = false;

            public $show_month = true;
            public $show_year = true;

        public function __construct($aData, $aTranslatedData, $aMonths=false, $aYears=false) {
            /*var_dump*/($this->data = $aData);
            /*var_dump*/($this->tdata = $aTranslatedData);
            /*var_dump*/($this->months = $aMonths);
            /*var_dump*/($this->years = $aYears);

        } //constructor

        protected function Find($aValue) {
            $index=0;
            foreach($this->tdata as $item) {
                if( $item==$aValue ) return $index;
                ++$index;
            } //foreach
            if( $aValue>0 ) return $this->Find($aValue-1);
            return false;
        } //Find

        public function Format($aLabel) {
            if( !in_array($aLabel, $this->months) && !in_array($aLabel, $this->years)) return '';
            $index = $this->Find($aLabel);
            //$index = $this->indexArr[$aLabel];

            $label = '';
            if( false!==$index && $this->show_month && in_array($aLabel, $this->months) ) $label .= DateConvert::Date('M', $this->data[$index]);
            if( false!==$index && $this->show_year && in_array($aLabel, $this->years) ) $label .= "\n" .DateConvert::Date('Y', $this->data[$index]);

            return $label;
        } //Format

    } //objDateLabelFomat
    //----------------------------------------------------------------------------------------------------
    class objMoneyLabelFormat {
            protected $data = false;

        public function __construct($aData) {
            $this->data = $aData;
        } //constructor

        public function Format($aLabel) {
            return number_format($aLabel);
        } //Format

    } //objMoneyLabelFomat

    //----------------------------------------------------------------------------------------------------

    abstract class objCalcModelPrototype {

            protected $_data = false;
            protected $flag_normalised_dates = false;

        public function __construct($aData=false) {
            $this->Set($aData);
        } //constructor

        public function Set($aData) {
            $this->_data = $this->Prepare($aData);
        } //Set

        public function Get() {
            return $this->_data;
        } //Get

        public function Delta() {
            $deltas = array();
            for($i=1; $i<count($this->_data['date']); $i++ )
                $deltas[] = $this->_data['date'][$i] - $this->_data['date'][$i-1];
            return min($deltas);
        } //Delta

        public function Period() {
            $d = $this->Delta();
            if( $d<60*60*24 ) return 'd';
            elseif( $d<60*60*24*7 )  return 'w';
            else return'm';
        } //Period

        public function isEmpty() {
            return empty($this->_data) || false==$this->_data;
        }//isEmpty

        public function Dump() {
            var_dump($this->_data);
        } //Dump

        public function MaxTranslatedDate() {
            $this->TranslatedDates();
            return max($this->_data['tdate']);
        } //MaxTranslatedDate

        public function MaxTranslatedBalance() {
            return max($this->TranslatedBalances());
        } //MaxTranslatedDate

        public function TranslatedDates() {
            if( empty($this->_data['tdate']) ) $this->_data['tdate'] = range(0, count($this->_data['date'])-1);
            return $this->_data['tdate'];
        } //TranslatedDates

        public function TranslatedBalances() {
            return range(0, count($this->_data['balance'])-1);
        } //TranslatedBalances

        public function Dates() {
            return $this->_data['date'];
        } //Dates

        public function Balances() {
            return $this->_data['balance'];
        } //Balances

        public function MinBalance() {
            return min($this->_data['balance']);
        } //MinBalance

        public function MaxBalance() {
            return max($this->_data['balance']);
        } //MaxBalance

        public function StartDate() {
            return $this->_data['date'][0];
        } //StartDate

        public function FinishDate() {
            return $this->_data['date'][count($this->_data['date'])-1];
        } //FinishDate

        protected function Prepare($aData) {
            if( empty($aData) ) return false;
            $data = $aData;

            for($i=0; $i<count($data['date']); $i++) {
               $mdata[intval($data['date'][$i])] = floatval($data['balance'][$i]);
            }//for

            unset($data);
            ksort($mdata);
            $data = array();
            foreach($mdata as $idate => $ibalance) {
               $data['date'][] = $idate;
               $data['balance'][] = $ibalance;
            }//foreach

            return $data;
        } //Prepare

        abstract public function Build();

        protected function DivideDates($param) {
            $result=array(); $index=0;
            $prev=false;
            foreach($this->_data['date'] as $item ) {
                $p=DateConvert::Date($param, $item);
                if( $p!=$prev ) {
                    $result[]=$this->_data['tdate'][$index];
                    //$result[]=$index;
                    $prev=$p;
                }//if param is chenged
                ++$index;
            }//foreach
            //$result[] = count($this->_data['date'])-1;
            $result[] = $this->_data['tdate'][count($this->_data['tdate'])-1];
            return $result;
        } //DivideDates

        public function NormaliseDates($param, $force=false) {
            if( !$force && $this->flag_normalised_dates ) return $this->_data['tdate'];

            $countArr = array();
            foreach($this->_data['date'] as $item ) {
                $countArr[DateConvert::Date($param, $item)]++;
            }//foreach

            $cmax=max($countArr);
            $coefArr = array();
            foreach($countArr as $name=>$value) $coefArr[$name]=$cmax / $value;
/*

            echo"<pre>----------\n";
            echo "\nCmax= " ; var_export($cmax);
            echo "\ncountArr= " ; var_export($countArr);
            echo "\ncoefArr= " ; var_export($coefArr);
            echo"----------\n</pre>";
*/
            unset($countArr);

            $last=0;
            $tdates=array();
            $pindex=0;
            $prev=false;
            $nindex=-1;
//            echo "<pre>------------------------------------------------\n";
            foreach($this->_data['date'] as $item ) {

                $p=DateConvert::Date($param, $item);
                if( $p!=$prev ) {
                    $pindex=0;
                    $last=$nindex+1;
                    $prev=$p;
//                    echo "\n----------\n";
                }//if param is chenged
                //else {
                    $nindex = intval(round($pindex * $coefArr[$p] + $last));
                    $tdates[] = $nindex;
                    ++$pindex;
                //}
//                echo "\n iteration $p $nindex ";
            }//foreach
//            echo "\n------------------------------------------------\n</pre>";
            $this->_data['tdate'] = $tdates;
/*
            echo "<pre>------------------------------------------------\n";
            //echo var_export($nindex);
            //echo var_export(max($tdates));
            echo "\ntdates= "; var_export($tdates);
            echo "------------------------------------------------\n</pre>";
*/
            $this->flag_normalised_dates=true;
            return $this->_data['tdate'];
        } //NormaliseDates

        protected function FindCenters($arr) {
            $result = array();
            for($i=1; $i<count($arr); $i++) {
                $result[] = intval(0.5 * ($arr[$i] + $arr[$i-1]) );
            }//for
            return $result;
        } //FindCenters

        protected function ShiftValuesArr($arr, $shift=1) {
            $result=array();
            for($i=0; $i<count($arr); $i++) {
                $result[]=$arr[$i] + $shift;
            }
            return $result;
        } //ShiftValuesArr

        public function isOneYear() {
            return ( DateConvert::Date('Y', $this->_data['date'][1]) != DateConvert::Date('Y', $this->_data['date'][count($this->_data['date'])-1]) );
        }//isOneYear

    } //class CalcModelPrototype

    //-------------------------------------------------------------------------------------------------------
    //
    //-------------------------------------------------------------------------------------------------------
    class objCalcModel extends objCalcModelPrototype {

        public function Normalise(){
            $this->NormaliseDates('Ym');
        }//Normalise

        public function Build() {
            $this->NormaliseDates('Ym');
            /*var_dump*/($borders = $this->DivideDates('Y'));
            /*var_dump*/($mborders = $this->DivideDates('m'));

            $sborders = $borders;
            if( 'm'==$this->Period() ) {
                $sborders = $this->ShiftValuesArr($sborders, -1);
                array_shift($sborders);
                array_pop($sborders);
            }//special for mout period

            $mborders = $mborders;//$this->ShiftValuesArr($mborders, -1);
            /*var_dump*/($cborders = $this->FindCenters($borders));
            /*var_dump*/($cmborders = $this->FindCenters($mborders));

            return array('borders'=>$borders, 'sborders'=>$sborders, 'mborders'=>$mborders, 'cborders'=>$cborders, 'cmborders'=>$cmborders);
        } //Build

    } //class CalcModel

    //----------------------------------------------------------------------------------------------------
    class objCalcViewPrototype  extends objPlotView implements iErrorHandler, iVisible {

            protected $_error_str = array(
                                          'no error',
                                          'data is empty'
                                         );
            protected $Model = false;
            protected $_shift_labels = false;

        //-------------------------------------------------------------------------------------
        public function __construct($sizex=400, $sizey=300, $cachetime=false) {
            parent::__construct($sizex, $sizey, $cachetime);

            $this->Model = new objCalcModel();
        } //constructor

        public function ClassName() {
            return  __CLASS__;
        }//ClassName

        public function Dump() {
            $this->Model->Dump();
        } //Dump

        public function SetData($aData) {
            $this->Model->Set($aData['data']);
        } //SetData

        protected function BuildTitle() {
            $this->_graph->title->SetFont(FF_VERDANA, FS_BOLD,16);
            if ( $this->title )  {
            	$this->_graph->title->SetMargin(15);
                $this->_graph->title->Set(strtoupper($this->title));
             }
        } //BuildTitle

        public function ShiftLabels($aValue=true) {
            $this->_shift_labels = $aValue;
        } //ShiftLabels

        protected function BuildAxisX() {
            $max = intval($this->Model->MaxBalance());
            $min = intval($this->Model->MinBalance());
            $flag_down_graph = ( $min<0 );
            //----------------------------------------------------------------
            $this->_graph->xaxis->SetFont(FF_VERDANA, FS_NORMAL, 7.5);
            $this->_graph->xaxis->SetSelectedFont(FF_VERDANA, FS_BOLD, 7.5);
            $this->_graph->xaxis->title->SetFont(FF_VERDANA, FS_BOLD, 8);
            //----------------------------------------------------------------
            $this->_graph->xaxis->AdditionalLabels(true);
            $this->_graph->xaxis->setLabelAlign('center', 'bottoms');

            $this->_graph->xaxis->SetMajTickPositions(range(0, $this->Model->MaxTranslatedDate()));

            $this->_graph->xaxis->HideFirstTicklabel();
            $this->_graph->xaxis->HideTicks(true, true);
            if( $this->_shift_labels ) $this->_graph->xaxis->ShiftLabels();

            $this->_graph->xaxis->SetLabelMargin( ($flag_down_graph ? -20 : 15) );
            $this->_graph->xaxis->SetTitleMargin(10);
            //----------------------------------------------------------------
            $this->_graph->xgrid->Show(true, false);
            $this->_graph->xgrid->SetLineStyle('dashed');
            $this->_graph->xgrid->SetColor("white");
        }//BuildAxisY

        protected function BuildAxisY() {
        	//$this->_graph->SetScale('textlog',980,50000,0,0);

            // do not remove !!!
        	//$this->_graph->yaxis->scale->SetAutoMin(10000);
            //$this->_graph->yaxis->scale->SetAutoMax(26000);


            $max = intval($this->Model->MaxBalance());
            $min = intval($this->Model->MinBalance());
            $flag_down_graph = $min<0;
            $flag_up_graph = $max>0;

            $this->_graph->yaxis->scale->SetAutoMin( ($flag_down_graph ? $min : 0) );
            $this->_graph->yaxis->scale->SetAutoMax( ($flag_up_graph ? $max : 0) );

            $MoneyFormat = new objMoneyLabelFormat($this->Model->Balances());
            $this->_graph->yaxis->SetLabelFormatCallback(array($MoneyFormat,'format'));

            $this->_graph->yaxis->HideTicks(true, false);
            $this->_graph->yaxis->scale->SetGrace(($flag_up_graph ? 3 : 0), ($flag_down_graph ? 3 : 0));
            $this->_graph->yaxis->SetFont(FF_VERDANA, FS_NORMAL, 7.5);
            //$this->_graph->yaxis->HideFirstTickLabel();
            $this->_graph->yaxis->HideLastTickLabel();
            $this->_graph->yaxis->SetTitle('USD', 'high');
            $this->_graph->yaxis->SetTitleMargin(20);
            $this->_graph->yaxis->title->SetAngle(0);
            $this->_graph->yaxis->title->Align('center', 'bottom');
            $this->_graph->yaxis->title->SetFont(FF_VERDANA, FS_BOLD, 7.5);

            $this->_graph->ygrid->Show(false, false);
            $this->_graph->ygrid->SetLineStyle('dashed');
        }//BuildAxisY

        public function BuildGraph() {
          global $config;
            if ( $this->Model->isEmpty() )  throw new Exception($this->Error(1));
            $this->Model->Normalise();
            $this->_graph->SetImgFormat(strtoupper($this->type));

            if( $config['jpgraph']['antialiasing'] )  $this->_graph->img->SetAntiAliasing();
            $this->_graph->SetMarginColor('white');
            $this->_graph->SetMargin(60,20,40,40);
            $this->_graph->SetTickDensity(TICKD_DENSE, TICKD_DENSE); // Many Y-ticks

            $this->_graph->SetBox(false);
            $this->_graph->SetFrame(false);

            if( $this->_show_logo ) $this->BuildLogo();
            if( $this->_show_copyrigth ) $this->BuildCopyright();
            $this->title='';
            $this->BuildTitle();//add title
            //var_dump($this->Model->MaxTranslatedDate());
            //var_dump($this->Model->TranslatedDates());

            //var_dump($this->Model->MinBalance());
            //var_dump($this->Model->MaxBalance());

            $this->_graph->SetScale("linlin",
                                    0, 0,
                                    0, $this->Model->MaxTranslatedDate());

            //------------------------------------------------------------------
            $this->BuildAxisX();
            $this->BuildAxisY();
            //------------------------------------------------------------------
            $data_x = $this->Model->TranslatedDates();
            $data_y = $this->Model->Balances();
            //$data_x = array_merge(array($data_x[0]), $data_x);
            //$data_y = array_merge(array(intval($data_y[0]*0.9)), $data_y);
            //------------------------------------------------------------------
            $lineplot1 = new LinePlot($data_y, $data_x);
            $lineplot1->SetColor('#dce6be');
            $lineplot1->SetFillColor('#dce6be@0.4',true);
            $this->_graph->Add($lineplot1);

            $lineplot2 = new LinePlot($data_y, $data_x);
            $lineplot2->SetWeight(2);
            $lineplot2->SetColor('#aaaaaa@0.01');
            $this->_graph->Add($lineplot2);

            return true;
        } //BuildGraph

    }//CalcViewPrototype

    //----------------------------------------------------------------------------------------------------
    class objCalcView  extends objCalcViewPrototype implements iErrorHandler, iVisible {

            protected $_error_str = array(
                                          'no error',
                                          'data is empty'
                                         );


        //-------------------------------------------------------------------------------------
        public function __construct($sizex=400, $sizey=300, $cachetime=false) {
            parent::__construct($sizex, $sizey, $cachetime);

            $this->Model = new objCalcModel();
        } //constructor

        public function ClassName() {
            return  __CLASS__;
        }//ClassName

        protected function BuildAxisX() {
            parent::BuildAxisX();
            //------------------------------------------------------------------
            $border = $this->Model->Build();

            $this->_graph->xaxis->SetMajTickPositions(range(0, $this->Model->MaxTranslatedDate()));

            $DateFormat = new objDateLabelFormat($this->Model->Dates(), $this->Model->TranslatedDates(), $border['cmborders'], $border['cborders']);
            $DateFormat->show_month = !($this->Model->isOneYear());
            $this->_graph->xaxis->SetLabelFormatCallback(array($DateFormat, 'format'), $this->Model->TranslatedBalances());
            $this->_graph->xaxis->SetSelected($border['cborders']);
            $this->_graph->xgrid->SetSelectLinesX($border['sborders'], 'solid', array('white@0.19', 'white@0.19'));
            $this->_graph->xgrid->SetSelectLines2X($border['mborders'], 'solid', 'black');
            //$this->_graph->xaxis->HideTicks(false, false);
            //$this->_graph->xaxis->SetMajTickPositions($border['mborders']);
        }//BuildAxisX

    }//CalcView
    //----------------------------------------------------------------------------------------------------
    class objCalcPlotGenerator {
            protected $image_filename = false;
            protected $Calculator = false;

            public $width = 630;
            public $height = 300;
            public $mode = 'day';
            public $show_logo = false;
            public $show_copyright = false;
            public $clean_image = true;

        public function __construct($aCalculator) {
            $this->Calculator = $aCalculator;
        } //constructor

        public function __destruct() {
            if( $this->clean_image ) @unlink($this->image_filename);
        }//destructor

        public function GenerateID() {
            return md5(implode(', ', $this->Calculator->trades).'_'.$this->mode.'_'.DateConvert::Date('U'));
        } //GenerateID

        protected function Build() {
            $this->Calculator->getPairs();
            $this->Calculator->getForecastData($this->Calculator->timeRange[0],$this->Calculator->timeRange[1], false, $this->Calculator->trades);
            //---------------------------------------------------------------------------------------------------
            switch( $this->mode=strtoupper($this->mode) ) {
                case 'D':
                case 'DAY':
                    $this->_data = $this->Calculator->graphArrDetailed;
                    $this->_shift = false;
                break;
                //---------------------------------------------
                case 'W':
                case 'WEEK':
                    $this->Calculator->fillGraphArrWeek($this->Calculator->timeRange[0]);
                    $this->_data = $this->Calculator->graphArrWeek;
                    $this->_shift = false;
                break;
                //---------------------------------------------
                case 'M':
                case 'MONTH':
                    $this->Calculator->fillGraphArrMonth($this->Calculator->timeRange[0]);
                    $this->_data = $this->Calculator->graphArr;
                    $this->_shift = true;
                break;
            }//switch
        }//Build

        public function Render($prefix='calcplot') {
          global $config;
            $this->Build();

            $CalcPlotView = new objCalcView($this->width, $this->height);

            if( !$this->show_logo ) $CalcPlotView->HideLogo();
            if( !$this->show_copyright ) $CalcPlotView->HideCopyright();

            $CalcPlotView->SetData($this->_data);
            $CalcPlotView->ShiftLabels($this->_shift);

            $this->image_filename = $config['jpgraph']['cache_dir'] . $prefix .'_' . $this->GenerateID() . '.' . $CalcPlotView->type;

            $CalcPlotView->Render($this->image_filename);
            return $this->image_filename;
        }//Render
    } //class CalcPlotGenerator
//-------------------------------------------------------------------------------------------------------
?>
